﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Web;
using System.Web.Caching;

namespace Dexter.Caching.AspNet {
	public class AspNetCacheProvider : ICacheProvider {
		readonly Cache cache;
		Hashtable tags = Hashtable.Synchronized ( new Hashtable ( ) );

		/// <summary>
		/// Initializes a new instance of the <see cref="T:System.Object"/> class.
		/// </summary>
		public AspNetCacheProvider ( ) {
			if (HttpContext.Current == null) {
				throw new ArgumentException ( "Caching cannot be enabled when there is no current HttpContext.", "enabled" );
			}

			cache = HttpContext.Current.Cache;
		}

		/// <summary>
		/// Adds new object into the cache repository
		/// </summary>
		/// <param name="key">The cache key.</param>
		/// <param name="value">The object to add.</param>
		/// <param name="timeout">The timeout of the cache object.</param>
		/// <remarks>
		///		If the object will never expire, use <see cref="TimeSpan.Zero"/>
		/// </remarks>
		/// <exception cref="ArgumentException">Will be throw if <paramref name="key"/> is empty.</exception>
		/// <exception cref="ArgumentNullException">Will be throw if <paramref name="key"/> is null.</exception>
		public void Add ( string key , object value , TimeSpan timeout ) {
			key = Helper.GetKey ( key );

			cache.Add ( key , value , null , DateTime.Now.Add ( timeout ) , TimeSpan.Zero , CacheItemPriority.Normal , null );
		}

		/// <summary>
		/// Adds new object into the cache repository
		/// </summary>
		/// <param name="key">The cache key.</param>
		/// <param name="value">The object to add.</param>
		/// <param name="timeout">The timeout of the cache object.</param>
		/// <param name="tags">The tags of the object (it cloud be used for search).</param>
		/// <remarks>
		/// If the object will never expire, use <see cref="TimeSpan.Zero"/>
		/// </remarks>
		/// <exception cref="ArgumentException">Will be throw if <paramref name="key"/> is empty.</exception>
		/// <exception cref="ArgumentNullException">Will be throw if <paramref name="key"/> is null.</exception>
		public void Add ( string key , object value , TimeSpan timeout , string[] tags ) {

			throw new NotImplementedException ( );
		}

		/// <summary>
		/// Adds new object into the cache repository
		/// </summary>
		/// <param name="key">The cache key.</param>
		/// <param name="value">The object to add.</param>
		/// <param name="timeout">The timeout of the cache object.</param>
		/// <param name="region">The region name.</param>
		/// <param name="tags">The tags of the object (it cloud be used for search).</param>
		/// <remarks>
		/// If the object will never expire, use <see cref="TimeSpan.Zero"/>
		/// </remarks>
		/// <exception cref="ArgumentException">Will be throw if <paramref name="key"/> is empty.</exception>
		/// <exception cref="ArgumentNullException">Will be throw if <paramref name="key"/> is null.</exception>
		/// <exception cref="ArgumentException">Will be throw if <paramref name="region"/> is empty.</exception>
		/// <exception cref="ArgumentNullException">Will be throw if <paramref name="region"/> is null.</exception>
		public void Add ( string key , object value , TimeSpan timeout , string region , string[] tags ) {
			throw new NotImplementedException ( );
		}

		/// <summary>
		/// If the specified object doesn't exist into the repository, it will be added. Otherwise it will update the existing object.
		/// </summary>
		/// <param name="key">The cache key.</param>
		/// <param name="value">The object to add.</param>
		/// <param name="timeout">The timeout of the cache object.</param>
		/// <remarks>
		///		If the object will never expire, use <see cref="TimeSpan.Zero"/>
		/// </remarks>
		/// <exception cref="ArgumentException">Will be throw if <paramref name="key"/> is empty.</exception>
		/// <exception cref="ArgumentNullException">Will be throw if <paramref name="key"/> is null.</exception>
		public void Put ( string key , object value , TimeSpan timeout ) {
			key = Helper.GetKey ( key );

			cache.Insert ( key, value, null, DateTime.Now.Add ( timeout ), TimeSpan.Zero, CacheItemPriority.Normal, null );
		}

		/// <summary>
		/// If the specified object doesn't exist into the repository, it will be added. Otherwise it will update the existing object.
		/// </summary>
		/// <param name="key">The cache key.</param>
		/// <param name="value">The object to add.</param>
		/// <param name="timeout">The timeout of the cache object.</param>
		/// <param name="tags">The tags of the object (it cloud be used for search).</param>
		/// <remarks>
		/// If the object will never expire, use <see cref="TimeSpan.Zero"/>
		/// </remarks>
		/// <exception cref="ArgumentException">Will be throw if <paramref name="key"/> is empty.</exception>
		/// <exception cref="ArgumentNullException">Will be throw if <paramref name="key"/> is null.</exception>
		public void Put ( string key , object value , TimeSpan timeout , string[] tags ) {
			throw new NotImplementedException ( );
		}

		/// <summary>
		/// If the specified object doesn't exist into the repository, it will be added. Otherwise it will update the existing object.
		/// </summary>
		/// <param name="key">The cache key.</param>
		/// <param name="value">The object to add.</param>
		/// <param name="timeout">The timeout of the cache object.</param>
		/// <param name="region">The region name.</param>
		/// <param name="tags">The tags of the object (it cloud be used for search).</param>
		/// <remarks>
		/// If the object will never expire, use <see cref="TimeSpan.Zero"/>
		/// </remarks>
		/// <exception cref="ArgumentException">Will be throw if <paramref name="key"/> is empty.</exception>
		/// <exception cref="ArgumentNullException">Will be throw if <paramref name="key"/> is null.</exception>
		/// <exception cref="ArgumentException">Will be throw if <paramref name="region"/> is empty.</exception>
		/// <exception cref="ArgumentNullException">Will be throw if <paramref name="region"/> is null.</exception>
		public void Put ( string key , object value , TimeSpan timeout , string region , string[] tags ) {
			key = Helper.GetKey ( key, region );

			cache.Insert ( key, value, null, DateTime.Now.Add ( timeout ), Cache.NoSlidingExpiration, CacheItemPriority.Normal, null );
		}

		/// <summary>
		/// Return a set of cache objects with only one round-trip
		/// </summary>
		/// <param name="keys">All the cache key.</param>
		/// <param name="region">The cache region.</param>
		/// <returns>A collection of cache objects.</returns>
		/// <exception cref="ArgumentNullException">Will be throw if <paramref name="keys"/> is null.</exception>
		/// <exception cref="ArgumentException">Will be throw if <paramref name="region"/> is empty.</exception>
		/// <exception cref="ArgumentNullException">Will be throw if <paramref name="region"/> is null.</exception>
		public IEnumerable <KeyValuePair <string , object>> BulkGet ( IEnumerable <string> keys , string region ) {
			throw new NotImplementedException ( );
		}

		/// <summary>
		/// Remove all cache objects associated to the specified cache region.
		/// </summary>
		/// <param name="region">Name of the region.</param>
		/// <exception cref="ArgumentException">Will be throw if <paramref name="region"/> is empty.</exception>
		/// <exception cref="ArgumentNullException">Will be throw if <paramref name="region"/> is null.</exception>
		public void ClearRegion ( string region ) {
			IDictionaryEnumerator cacheEnum = cache.GetEnumerator ( );
			while (cacheEnum.MoveNext ( )) {
				var cacheKey = cacheEnum.Key.ToString ( );

				if(cacheKey.StartsWith ( region ))
					cache.Remove ( cacheKey );
			}
		}

		/// <summary>
		/// Retrieve the cache object for the specified cache key.
		/// </summary>
		/// <param name="key">The key.</param>
		/// <returns>The cache object</returns>
		/// <exception cref="ArgumentException">Will be throw if <paramref name="key"/> is empty.</exception>
		/// <exception cref="ArgumentNullException">Will be throw if <paramref name="key"/> is null.</exception>
		public object Get ( string key ) {
			key = Helper.GetKey ( key );
			return cache.Get ( key );
		}

		/// <summary>
		/// Retrieve the cache object for the specified cache key and specified region.
		/// </summary>
		/// <param name="key">The key.</param>
		/// <param name="region">The region name.</param>
		/// <returns>The cache object</returns>
		/// <exception cref="ArgumentException">Will be throw if <paramref name="key"/> is empty.</exception>
		/// <exception cref="ArgumentNullException">Will be throw if <paramref name="key"/> is null.</exception>
		/// <exception cref="ArgumentException">Will be throw if <paramref name="region"/> is empty.</exception>
		/// <exception cref="ArgumentNullException">Will be throw if <paramref name="region"/> is null.</exception>
		public object Get ( string key , string region ) {
			key = Helper.GetKey ( key, region );
			throw new NotImplementedException ( );
		}

		/// <summary>
		/// Retrieve the cache object for the specified cache key.
		/// </summary>
		/// <param name="key">The key.</param>
		/// <returns>The cache object</returns>
		/// <exception cref="ArgumentException">Will be throw if <paramref name="key"/> is empty.</exception>
		/// <exception cref="ArgumentNullException">Will be throw if <paramref name="key"/> is null.</exception>
		public T Get <T> ( string key ) {
			key = Helper.GetKey ( key );
			return ( T ) cache.Get ( key );
		}

		/// <summary>
		/// Retrieve the cache object for the specified cache key and specified region.
		/// </summary>
		/// <param name="key">The key.</param>
		/// <param name="region">The region name.</param>
		/// <returns>The cache object</returns>
		/// <exception cref="ArgumentException">Will be throw if <paramref name="key"/> is empty.</exception>
		/// <exception cref="ArgumentNullException">Will be throw if <paramref name="key"/> is null.</exception>
		/// <exception cref="ArgumentException">Will be throw if <paramref name="region"/> is empty.</exception>
		/// <exception cref="ArgumentNullException">Will be throw if <paramref name="region"/> is null.</exception>
		public T Get <T> ( string key , string region ) {
			key = Helper.GetKey ( key, region );
			return ( T )cache.Get ( key );
		}

		/// <summary>
		/// Retrieve all cache object with the specified tag and the specified region.
		/// </summary>
		/// <param name="tag">The tags name.</param>
		/// <param name="region">The region name.</param>
		/// <returns>A set of cache object.</returns>
		/// <exception cref="ArgumentException">Will be throw if <paramref name="tag"/> is empty.</exception>
		/// <exception cref="ArgumentNullException">Will be throw if <paramref name="tag"/> is null.</exception>
		/// <exception cref="ArgumentException">Will be throw if <paramref name="region"/> is empty.</exception>
		/// <exception cref="ArgumentNullException">Will be throw if <paramref name="region"/> is null.</exception>
		public IEnumerable <KeyValuePair <string , object>> GetObjectsByTag ( string tag , string region ) {
			throw new NotImplementedException ( );
		}

		/// <summary>
		/// Returns a list of objects that have tags matching all of the tags provided in the parameter of this method.
		/// </summary>
		/// <param name="tags">The tags name.</param>
		/// <param name="region">The region name.</param>
		/// <returns>A set of cache object.</returns>
		/// <exception cref="ArgumentNullException">Will be throw if <paramref name="tags"/> is null.</exception>
		/// <exception cref="ArgumentException">Will be throw if <paramref name="region"/> is empty.</exception>
		/// <exception cref="ArgumentNullException">Will be throw if <paramref name="region"/> is null.</exception>
		public IEnumerable <KeyValuePair <string , object>> GetObjectsByAllTags ( string[] tags , string region ) {
			throw new NotImplementedException ( );
		}

		/// <summary>
		/// Returns a list of objects that have tags matching any of the tags provided in the parameter of this method.
		/// </summary>
		/// <param name="tags">The tags.</param>
		/// <param name="region">The region.</param>
		/// <returns>A set of cache object.</returns>
		/// <exception cref="ArgumentNullException">Will be throw if <paramref name="tags"/> is null.</exception>
		/// <exception cref="ArgumentException">Will be throw if <paramref name="region"/> is empty.</exception>
		/// <exception cref="ArgumentNullException">Will be throw if <paramref name="region"/> is null.</exception>
		public IEnumerable <KeyValuePair <string , object>> GetObjectsByAnyTags ( string[] tags , string region ) {
			throw new NotImplementedException ( );
		}

		/// <summary>
		/// Returns a list of all objects in a region. This method is useful when you do not know all the tags used in the region.
		/// </summary>
		/// <param name="region">The region name.</param>
		/// <returns>A set of cache object.</returns>
		/// <exception cref="ArgumentException">Will be throw if <paramref name="region"/> is empty.</exception>
		/// <exception cref="ArgumentNullException">Will be throw if <paramref name="region"/> is null.</exception>
		public IEnumerable <KeyValuePair <string , object>> GetObjectsInRegion ( string region ) {
			throw new NotImplementedException ( );
		}

		/// <summary>
		/// Remove a cache object with the specified key from the cache repository.
		/// </summary>
		/// <param name="key">The key.</param>
		/// <exception cref="ArgumentException">Will be throw if <paramref name="key"/> is empty.</exception>
		/// <exception cref="ArgumentNullException">Will be throw if <paramref name="key"/> is null.</exception>
		public void Remove ( string key ) {
			key = Helper.GetKey ( key );
			cache.Remove ( key );
		}

		/// <summary>
		/// Remove a cache object with the specified key and the specified region from the cache repository.
		/// </summary>
		/// <param name="key">The key.</param>
		/// <param name="region">The region.</param>
		/// <exception cref="ArgumentException">Will be throw if <paramref name="key"/> is empty.</exception>
		/// <exception cref="ArgumentNullException">Will be throw if <paramref name="key"/> is null.</exception>
		/// <exception cref="ArgumentException">Will be throw if <paramref name="region"/> is empty.</exception>
		/// <exception cref="ArgumentNullException">Will be throw if <paramref name="region"/> is null.</exception>
		public void Remove ( string key , string region ) {
			key = Helper.GetKey ( key, region );
			cache.Remove ( key );
		}
	}
}
